// SPDX-License-Identifier: GPL-2.0

/**
 * Copyright (C) 2025 Huawei Technologies Co., Ltd.
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, see <https://www.gnu.org/licenses/>.
 */

#include <errno.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>

#include "cmanuapi_emu.h"

/* Hard-code and non-multithread implementation is probably OK since that is mostly for testing purposes */
#define NUM_CPUS    128
#define MAX_L2_MASK 0xFF
#define L2_CWS      8

typedef struct CPU_EMU
{
    struct
    {
        unsigned long dmask;
        unsigned long imask;
    } l2;
} CPU_t;

static CPU_t* cpus_EMU = NULL;

void fini_EMU(void)
{
    free(cpus_EMU);
    cpus_EMU = NULL;

    fd = -1;
}

int cman_uapi_init_EMU(void)
{
    size_t i = 0;

    cpus_EMU = (CPU_t*)malloc(sizeof(CPU_t) * NUM_CPUS);

    for (i = 0; i < NUM_CPUS; i++)
    {
        cpus_EMU[i].l2.dmask = 0;
        cpus_EMU[i].l2.imask = 0;
    }

    fd = 0;

    return 0;
}

ssize_t cman_uapi_l2_partition_get_EMU(int cpu, unsigned long* dmask, unsigned long* imask)
{
    if ((cpu < 0) || (cpu >= NUM_CPUS) || dmask == NULL || imask == NULL)
    {
        errno = EINVAL;
        return -1;
    }

    *dmask = cpus_EMU[cpu].l2.dmask;
    *imask = cpus_EMU[cpu].l2.imask;

    return 0;
}

ssize_t cman_uapi_l2_partition_set_EMU(int cpu, unsigned long dmask, unsigned long imask)
{
    unsigned long sidp_imask = 0, sidp_dmask = 0;
    int dcount = 0, icount = 0;
    sidp_dmask = dmask;
    sidp_imask = imask;

    if ((cpu < -1) || (cpu >= NUM_CPUS))
    {
        errno = EINVAL;
        return -1;
    }

    if (dmask > MAX_L2_MASK || imask > MAX_L2_MASK)
    {
        errno = EINVAL;
        return -1;
    }

    while (sidp_dmask) {
        dcount += sidp_dmask & 1;
        sidp_dmask >>= 1;
    }
    if (dcount > (L2_CWS - 2))
    {
        errno = EPERM;
        return -1;
    }

    while (sidp_imask) {
        icount += sidp_imask & 1;
        sidp_imask >>= 1;
    }
    if (icount > (L2_CWS - 2))
    {
        errno = EPERM;
        return -1;
    }

    if (cpu == -1)
    {
        for (int i = 0; i < NUM_CPUS; i++)
        {
            cpus_EMU[i].l2.dmask = dmask;
            cpus_EMU[i].l2.imask = imask;
        }
    }
    else
    {
        cpus_EMU[cpu].l2.dmask = dmask;
        cpus_EMU[cpu].l2.imask = imask;
    }

    return 0;
}

hw_info_ioctl_args_t cman_uapi_hw_info_EMU(void)
{
    hw_info_ioctl_args_t args = {
        .l2_segment_size = 64 * 1024,
        .l2_capacity = 512 * 1024 };

    return args;
}

int cman_uapi_reset_l2_partition_EMU(int cpu)
{
    errno = 0;

    if ((cpu < -1) || (cpu >= NUM_CPUS))
    {
        errno = EINVAL;
        return -1;
    }
    else if(cpu >=0)
    {
        cpus_EMU[cpu].l2.dmask = 0;
        cpus_EMU[cpu].l2.imask = 0;
    }
    else
    {
        for (size_t i = 0; i < NUM_CPUS; i++)
        {
            cpus_EMU[i].l2.dmask = 0;
            cpus_EMU[i].l2.imask = 0;
        }
    }

    return 0;
}
